package com.gobang.service.impl;

import com.gobang.common.util.MatchUtils;
import com.gobang.domain.entity.MatchQueueEntry;
import com.gobang.service.MatchAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 基于积分的匹配算法实现
 * 根据用户积分进行就近匹配，保证对局公平性
 *
 * @author gobang-team
 * @since 1.0.0
 */
@Component
@Slf4j
public class ScoreBasedMatchAlgorithm implements MatchAlgorithm {

    @Override
    public MatchQueueEntry findMatch(MatchQueueEntry currentUser, List<MatchQueueEntry> waitingQueue) {
        if (currentUser == null || waitingQueue == null || waitingQueue.isEmpty()) {
            return null;
        }

        log.debug("为用户{}查找匹配对手，当前积分：{}，队列大小：{}", 
                currentUser.getUserId(), currentUser.getCurrentScore(), waitingQueue.size());

        // 计算动态匹配阈值
        long waitTime = currentUser.calculateWaitTime();
        int threshold = MatchUtils.calculateDynamicThreshold(waitTime);

        log.debug("用户{}等待时间：{}秒，动态阈值：{}", currentUser.getUserId(), waitTime, threshold);

        // 查找最佳匹配对手
        MatchQueueEntry bestMatch = null;
        // 希望匹配分值最小，
        // 匹配分值代表优先级：分值越小，优先级越高！！
        double bestScore = Double.MAX_VALUE;

        for (MatchQueueEntry candidate : waitingQueue) {
            // 跳过自己
            if (candidate.getUserId().equals(currentUser.getUserId())) {
                continue;
            }

            // 检查是否满足匹配条件
            if (isMatch(currentUser, candidate)) {
                // 计算匹配分值
                double matchScore = calculateMatchScore(currentUser, candidate);
                if (matchScore < bestScore) {
                    bestScore = matchScore;
                    bestMatch = candidate;
                }
            }
        }

        if (bestMatch != null) {
            log.info("为用户{}找到匹配对手{}，积分差：{}，匹配度：{:.2f}", 
                    currentUser.getUserId(), bestMatch.getUserId(), 
                    Math.abs(currentUser.getCurrentScore() - bestMatch.getCurrentScore()), bestScore);
        } else {
            log.debug("用户{}暂未找到合适的对手", currentUser.getUserId());
        }

        return bestMatch;
    }

    @Override
    public List<MatchPair> batchMatch(List<MatchQueueEntry> waitingQueue) {
        if (waitingQueue == null || waitingQueue.size() < 2) {
            return new ArrayList<>();
        }

        log.info("开始批量匹配，队列大小：{}", waitingQueue.size());

        // 匹配成功的用户对 [{1,2},{3,4}]
        List<MatchPair> matchPairs = new ArrayList<>();
        // 临时全量匹配队列（按照用户加入队列时间排序，先匹配的在前面）
        List<MatchQueueEntry> remainingUsers = new ArrayList<>(waitingQueue);

        // 按加入时间排序，优先处理等待时间长的用户
        remainingUsers.sort(Comparator.comparing(MatchQueueEntry::getJoinTime));

        while (remainingUsers.size() >= 2) {
            // 获取第一个加入队列用户（等待时间最长）
            MatchQueueEntry currentUser = remainingUsers.remove(0);
            // 获取当前用的匹配的对手
            MatchQueueEntry matchedUser = findMatch(currentUser, remainingUsers);

            if (matchedUser != null) {
                // 找到匹配，创建匹配对
                double matchScore = calculateMatchScore(currentUser, matchedUser);
                MatchPair pair = new MatchPair(currentUser, matchedUser, matchScore);
                matchPairs.add(pair);

                // 从队列中移除已匹配的用户
                remainingUsers.remove(matchedUser);

                log.info("成功匹配：{}", pair);
            }
        }

        log.info("批量匹配完成，成功匹配{}对，剩余用户{}个", matchPairs.size(), remainingUsers.size());
        return matchPairs;
    }

    @Override
    public double calculateMatchScore(MatchQueueEntry user1, MatchQueueEntry user2) {
        // 到这里，俩用户已经可以匹配了，要算具体的匹配分值

        if (user1 == null || user2 == null) {
            return Double.MAX_VALUE;
        }

        // 积分差值权重（越小越好）
        int scoreDiff = MatchUtils.calculateScoreDifference(user1.getCurrentScore(), user2.getCurrentScore());
        double scoreWeight = scoreDiff * 1.0;

        // 等待时间权重（等待时间长的用户优先级高）
        long user1WaitTime = user1.calculateWaitTime();
        long user2WaitTime = user2.calculateWaitTime();
        double waitTimeWeight = (user1WaitTime + user2WaitTime) * -0.1; // 负权重，等待时间越长权重越小

        // 综合评分（越小越好）
        // 总分 = 积分差 - 俩用户等待总时长
        double totalScore = scoreWeight + waitTimeWeight;

        log.debug("匹配度计算：用户{}vs用户{}，积分差：{}，等待时间：{}+{}，总分：{:.2f}", 
                user1.getUserId(), user2.getUserId(), scoreDiff, user1WaitTime, user2WaitTime, totalScore);

        return totalScore;
    }

    @Override
    public boolean isMatch(MatchQueueEntry user1, MatchQueueEntry user2) {
        if (user1 == null || user2 == null) {
            return false;
        }

        // 不能匹配自己
        if (user1.getUserId().equals(user2.getUserId())) {
            return false;
        }

        // 计算积分差值
        int scoreDiff = MatchUtils.calculateScoreDifference(
                user1.getCurrentScore(), user2.getCurrentScore());

        // 根据等待时间计算动态阈值
        long user1WaitTime = user1.calculateWaitTime();
        long user2WaitTime = user2.calculateWaitTime();
        long maxWaitTime = Math.max(user1WaitTime, user2WaitTime);

        // 拿到较大的动态阈值
        int threshold = MatchUtils.calculateDynamicThreshold(maxWaitTime);

        // 分差小于阈值，认为可以匹配
        boolean canMatch = scoreDiff <= threshold;

        log.debug("匹配判断：用户{}({})vs用户{}({})，积分差：{}，阈值：{}，结果：{}", 
                user1.getUserId(), user1.getCurrentScore(), 
                user2.getUserId(), user2.getCurrentScore(), 
                scoreDiff, threshold, canMatch);

        return canMatch;
    }

    /**
     * 获取推荐匹配列表
     * 为用户获取可能的匹配对手列表（按匹配度排序）
     *
     * @param currentUser 当前用户
     * @param waitingQueue 等待队列
     * @param limit 返回数量限制
     * @return 推荐匹配列表
     */
    public List<MatchQueueEntry> getRecommendedMatches(MatchQueueEntry currentUser, 
                                                       List<MatchQueueEntry> waitingQueue, 
                                                       int limit) {
        if (currentUser == null || waitingQueue == null || waitingQueue.isEmpty()) {
            return new ArrayList<>();
        }

        return waitingQueue.stream()
                .filter(candidate -> !candidate.getUserId().equals(currentUser.getUserId()))
                .filter(candidate -> isMatch(currentUser, candidate))
                .sorted((u1, u2) -> Double.compare(
                        calculateMatchScore(currentUser, u1),
                        calculateMatchScore(currentUser, u2)))
                .limit(limit)
                .collect(Collectors.toList());
    }

    /**
     * 统计匹配算法性能
     *
     * @param waitingQueue 等待队列
     * @return 统计信息
     */
    public MatchStatistics getMatchStatistics(List<MatchQueueEntry> waitingQueue) {
        if (waitingQueue == null || waitingQueue.isEmpty()) {
            return new MatchStatistics(0, 0, 0.0, 0.0);
        }

        int totalUsers = waitingQueue.size();
        long totalWaitTime = waitingQueue.stream()
                .mapToLong(MatchQueueEntry::calculateWaitTime)
                .sum();
        double avgWaitTime = (double) totalWaitTime / totalUsers;
        
        // 计算积分分布范围
        int minScore = waitingQueue.stream()
                .mapToInt(MatchQueueEntry::getCurrentScore)
                .min().orElse(0);
        int maxScore = waitingQueue.stream()
                .mapToInt(MatchQueueEntry::getCurrentScore)
                .max().orElse(0);
        double scoreRange = maxScore - minScore;

        return new MatchStatistics(totalUsers, totalWaitTime, avgWaitTime, scoreRange);
    }

    /**
     * 匹配统计信息
     */
    public static class MatchStatistics {
        private final int totalUsers;
        private final long totalWaitTime;
        private final double avgWaitTime;
        private final double scoreRange;

        public MatchStatistics(int totalUsers, long totalWaitTime, double avgWaitTime, double scoreRange) {
            this.totalUsers = totalUsers;
            this.totalWaitTime = totalWaitTime;
            this.avgWaitTime = avgWaitTime;
            this.scoreRange = scoreRange;
        }

        public int getTotalUsers() { return totalUsers; }
        public long getTotalWaitTime() { return totalWaitTime; }
        public double getAvgWaitTime() { return avgWaitTime; }
        public double getScoreRange() { return scoreRange; }

        @Override
        public String toString() {
            return String.format("MatchStatistics{users=%d, totalWait=%ds, avgWait=%.1fs, scoreRange=%.0f}", 
                    totalUsers, totalWaitTime, avgWaitTime, scoreRange);
        }
    }
} 